// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	10/18/2014 1:21:49 AM				
// -----------------------------

#include "DefaultMaterial.h"

#include "Application.h"
#include "FileManager.h"
#include "RenderManager.h"

struct MatrixBuffer
{
	Matrix World;
	Matrix WorldViewProj;
	Matrix View;
};

struct MaterialBuffer
{
	Vector3	Color;
	Float	Roughness;
	Vector4	Ambient;
	Float	Specular;
	Bool	UseTexture;
	Vector2	Padding;
};

DefaultMaterial::DefaultMaterial(Application* lpApp)
	: Material(lpApp, RenderType::Deferred, false)
	, m_Diffuse(0.5f, 0.5f, 0.5f)
	, m_Ambient(0.2f, 0.2f, 0.2f)
	, m_Roughness(1.0f)
	, m_Specular(0.0f)
	, m_lpVertexShader(nullptr)
	, m_lpMatrixBuffer(nullptr)
	, m_lpPixelShader(nullptr)
	, m_lpMaterialBuffer(nullptr)
	, m_lpInputLayout(nullptr)
	, m_lpDiffuseTexture(nullptr)
	, m_lpSamplerState(nullptr)
{
	Init(lpApp);
}
DefaultMaterial::DefaultMaterial(Application* lpApp, const Color& diffuse, const Color& ambient, Float roughness, Float specular)
	: Material(lpApp, RenderType::Deferred, false)
	, m_Diffuse(diffuse)
	, m_Ambient(ambient)
	, m_Roughness(roughness)
	, m_Specular(specular)
	, m_lpVertexShader(nullptr)
	, m_lpMatrixBuffer(nullptr)
	, m_lpPixelShader(nullptr)
	, m_lpMaterialBuffer(nullptr)
	, m_lpInputLayout(nullptr)
	, m_lpDiffuseTexture(nullptr)
	, m_lpSamplerState(nullptr)
{
	Init(lpApp);
}
DefaultMaterial::~DefaultMaterial()
{
	NullRelease(&m_lpSamplerState);
	NullRelease(&m_lpInputLayout);
	NullRelease(&m_lpMaterialBuffer);
	NullRelease(&m_lpPixelShader);
	NullRelease(&m_lpMatrixBuffer);
	NullRelease(&m_lpVertexShader);
}

void DefaultMaterial::Init(Application* lpApp)
{
	TString vertexShader = lpApp->GetFiles()->GetFileName(_T("Shader_DefaultVertex"));
	TString pixelShader = lpApp->GetFiles()->GetFileName(_T("Shader_DefaultPixel"));

	m_lpVertexShader = ShaderCompiler::LoadVertexFromFile(m_lpRenderManager->GetDevice(), vertexShader.c_str());
	m_lpMatrixBuffer = IBuffer::Create(m_lpRenderManager->GetDevice(), BufferType::Constant, Usage::Default, 0, sizeof(MatrixBuffer), 1, nullptr);

	m_lpPixelShader = ShaderCompiler::LoadPixelFromFile(m_lpRenderManager->GetDevice(), pixelShader.c_str());
	m_lpMaterialBuffer = IBuffer::Create(m_lpRenderManager->GetDevice(), BufferType::Constant, Usage::Default, 0, sizeof(MaterialBuffer), 1, nullptr);

	m_lpInputLayout = IInputLayout::Create(m_lpRenderManager->GetDevice(), m_lpVertexShader);
	m_lpSamplerState = ISamplerState::Create(m_lpRenderManager->GetDevice(), AddressMode::Wrap, AddressMode::Wrap, AddressMode::Wrap);
}

void DefaultMaterial::Apply(Application* lpApp)
{
	IRenderDevice* lpDevice = m_lpRenderManager->GetDevice();

	// Bind Vertex
	lpDevice->SetVertexShader(m_lpVertexShader);
	m_lpVertexShader->SetBufferAsConstant(0, m_lpMatrixBuffer);

	// Bind Pixel
	lpDevice->SetPixelShader(m_lpPixelShader);
	m_lpPixelShader->SetBufferAsConstant(0, m_lpMaterialBuffer);
	m_lpPixelShader->SetSamplerState(0, m_lpSamplerState);
	if (m_lpDiffuseTexture)
	{
		m_lpPixelShader->SetTextureAsResource(0, m_lpDiffuseTexture.Get());
	}
	else
		m_lpPixelShader->ClearResource(0);

	// Unbind Any Others
	lpDevice->SetGeometryShader(nullptr);
	lpDevice->SetHullShader(nullptr);
	lpDevice->SetDomainShader(nullptr);

	// Bind InputManager & Topology
	lpDevice->SetInputLayout(m_lpInputLayout);
	lpDevice->SetTopology(Topology::TriangleList);
}
void DefaultMaterial::UpdateMatrices(Application* lpApp)
{
	// Update the Matrix Buffer
	MatrixBuffer matrixBuffer = {};
	matrixBuffer.World = m_World;
	matrixBuffer.View = m_View;
	matrixBuffer.WorldViewProj = m_World * m_View * m_Projection;
	m_lpMatrixBuffer->Update(&matrixBuffer, sizeof(MatrixBuffer));
}
void DefaultMaterial::UpdateData(Application* lpApp)
{
	MaterialBuffer matBuffer = {};
	matBuffer.Color = m_Diffuse.ToVector3();
	matBuffer.Ambient = m_Ambient.ToVector4();
	matBuffer.Specular = m_Specular;
	matBuffer.Roughness = m_Roughness;
	matBuffer.UseTexture = (m_lpDiffuseTexture) ? 1 : 0;
	m_lpMaterialBuffer->Update(&matBuffer, sizeof(MaterialBuffer));
}

void DefaultMaterial::SetDiffuseTexture(const RefPtr<ITexture2D>& lpTexture)
{
	m_lpDiffuseTexture = lpTexture;
}